What is p-timeout?
The p-timeout package is a utility that allows you to wrap a Promise with a timeout, after which a specified exception is thrown if the promise does not resolve or reject before the timeout period elapses. This is useful for ensuring that your application can handle situations where a Promise might take too long to complete, or when you want to enforce a strict timing policy for certain operations.
What are p-timeout's main functionalities?
Timeout a Promise
This feature allows you to set a timeout for a Promise. If the Promise does not resolve or reject within the specified time, it will be rejected with a TimeoutError.
const pTimeout = require('p-timeout');
const doSomething = () => new Promise(resolve => setTimeout(resolve, 2000));
pTimeout(doSomething(), 1000).then(() => console.log('Resolved')).catch(error => console.log('Timed out:', error.message));
Custom Timeout Error
This feature allows you to specify a custom error message for the timeout. If the Promise times out, it will be rejected with a TimeoutError that contains your custom message.
const pTimeout = require('p-timeout');
const TimeoutError = pTimeout.TimeoutError;
const doSomething = () => new Promise(resolve => setTimeout(resolve, 2000));
pTimeout(doSomething(), 1000, 'Operation timed out').then(() => console.log('Resolved')).catch(error => {
if (error instanceof TimeoutError) {
console.log('Custom timeout message:', error.message);
}
});
Clearing the Timeout
This feature allows you to clear the timeout before it occurs. This is useful if the conditions of your operation change and you no longer require the timeout.
const pTimeout = require('p-timeout');
const doSomething = () => new Promise(resolve => setTimeout(resolve, 2000));
const promiseWithTimeout = pTimeout(doSomething(), 1000);
promiseWithTimeout.then(() => console.log('Resolved')).catch(error => console.log('Timed out:', error.message));
// Later, if you decide you no longer want the timeout:
pTimeout.clear(promiseWithTimeout);
Other packages similar to p-timeout
promise-timeout
The promise-timeout package offers similar functionality to p-timeout, allowing you to add a timeout to a Promise. It differs in API design and error handling specifics.
bluebird
Bluebird is a full-featured Promise library that includes a .timeout() method for timing out promises. It is more comprehensive than p-timeout but also larger in size and scope.
async-timeout
The async-timeout package is similar to p-timeout but is designed specifically for use with async functions and the async/await syntax.
p-timeout
Timeout a promise after a specified amount of time
[!NOTE]
You may want to use AbortSignal.timeout()
instead. Learn more.
Install
npm install p-timeout
Usage
import {setTimeout} from 'node:timers/promises';
import pTimeout from 'p-timeout';
const delayedPromise = setTimeout(200);
await pTimeout(delayedPromise, {
milliseconds: 50,
});
API
pTimeout(input, options)
Returns a decorated input
that times out after milliseconds
time. It has a .clear()
method that clears the timeout.
If you pass in a cancelable promise, specifically a promise with a .cancel()
method, that method will be called when the pTimeout
promise times out.
input
Type: Promise
Promise to decorate.
options
Type: object
milliseconds
Type: number
Milliseconds before timing out.
Passing Infinity
will cause it to never time out.
message
Type: string | Error | false
Default: 'Promise timed out after 50 milliseconds'
Specify a custom error message or error to throw when it times out:
message: 'too slow'
will throw TimeoutError('too slow')
message: new MyCustomError('it’s over 9000')
will throw the same error instancemessage: false
will make the promise resolve with undefined
instead of rejecting
If you do a custom error, it's recommended to sub-class TimeoutError
:
import {TimeoutError} from 'p-timeout';
class MyCustomError extends TimeoutError {
name = "MyCustomError";
}
fallback
Type: Function
Do something other than rejecting with an error on timeout.
You could for example retry:
import {setTimeout} from 'node:timers/promises';
import pTimeout from 'p-timeout';
const delayedPromise = () => setTimeout(200);
await pTimeout(delayedPromise(), {
milliseconds: 50,
fallback: () => {
return pTimeout(delayedPromise(), {milliseconds: 300});
},
});
customTimers
Type: object
with function properties setTimeout
and clearTimeout
Custom implementations for the setTimeout
and clearTimeout
functions.
Useful for testing purposes, in particular to work around sinon.useFakeTimers()
.
Example:
import {setTimeout} from 'node:timers/promises';
import pTimeout from 'p-timeout';
const originalSetTimeout = setTimeout;
const originalClearTimeout = clearTimeout;
sinon.useFakeTimers();
await pTimeout(doSomething(), {
milliseconds: 2000,
customTimers: {
setTimeout: originalSetTimeout,
clearTimeout: originalClearTimeout
}
});
signal
Type: AbortSignal
You can abort the promise using AbortController
.
Requires Node.js 16 or later.
import pTimeout from 'p-timeout';
import delay from 'delay';
const delayedPromise = delay(3000);
const abortController = new AbortController();
setTimeout(() => {
abortController.abort();
}, 100);
await pTimeout(delayedPromise, {
milliseconds: 2000,
signal: abortController.signal
});
TimeoutError
Exposed for instance checking and sub-classing.
Related
- delay - Delay a promise a specified amount of time
- p-min-delay - Delay a promise a minimum amount of time
- p-retry - Retry a promise-returning function
- More…
AbortSignal
Modern alternative to p-timeout
Asynchronous functions like fetch
can accept an AbortSignal
, which can be conveniently created with AbortSignal.timeout()
.
The advantage over p-timeout
is that the promise-generating function (like fetch
) is actually notified that the user is no longer expecting an answer, so it can interrupt its work and free resources.
const response = await fetch('./my-api', {signal: AbortSignal.timeout(5000)});
async function buildWall(signal) {
for (const brick of bricks) {
signal.throwIfAborted();
await layBrick();
}
}
await buildWall(AbortSignal.timeout(60_000))
You can also combine multiple signals, like when you have a timeout and an AbortController
triggered with a “Cancel” button click. You can use the upcoming AbortSignal.any()
helper or abort-utils
.